;;; Code:

;; -------- lsp-mode --------
;; github.com/emacs-lsp/lsp-mode/#supported-languages
;; & mads-hartmann/bash-language-server config
(use-package lsp-mode
  :defer 3
  :commands lsp
  :init
  (setq-default lsp-restart 'auto-restart)
  (setq lsp-auto-guess-root t        ; Detect project root
	    lsp-keep-workspace-alive nil ; Auto-kill LSP server
	    lsp-prefer-flymake nil       ; Use lsp-ui and flycheck
	    flymake-fringe-indicator-position 'right-fringe)
  ;; THE lsp hook now goes here.
  :hook
  (prog-mode . (lambda ()
		         (unless (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
		           (lsp-deferred))))
  :bind (:map lsp-mode-map
	          ("C-c C-d" . lsp-describe-thing-at-point)))

(use-package lsp-ui
  :custom-face
  (lsp-ui-doc-background ((t (:background ,(face-background 'tooltip)))))
  (lsp-ui-sideline-code-action ((t (:inherit font-lock-type-face))))
  :init
  (setq lsp-ui-doc-enable t
	lsp-ui-doc-use-webkit nil
	lsp-ui-doc-delay 0.2
	lsp-ui-doc-border (face-foreground 'default)
	lsp-eldoc-enable-hover nil ;; Disable eldoc in minibuffer
	lsp-ui-sideline-enable t
	lsp-ui-sideline-show-hover nil
	lsp-ui-sideline-show-diagnostics nil
	lsp-ui-sideline-ignore-duplicate t
	lsp-ui-imenu-enable t
	lsp-ui-imenu-colors `(,(face-foreground 'font-lock-keyword-face)
			      ,(face-foreground 'font-lock-string-face)
			      ,(face-foreground 'font-lock-constant-face)
			      ,(face-foreground 'font-lock-variable-name-face)))
  :hook
  (lsp-mode . lsp-ui-mode)
  :config
  (setq lsp-ui-doc-max-height 20
	lsp-ui-doc-max-width 50
	lsp-ui-sideline-ignore-duplicate t
	lsp-ui-peek-always-show t)
  (add-to-list 'lsp-ui-doc-frame-parameters '(right-fringe . 8))
  ;; `C-g'to close doc
  (advice-add #'keyboard-quit :before #'lsp-ui-doc-hide)
  ;; Reset `lsp-ui-doc-background' after loading theme
  (add-hook 'after-load-theme-hook
	    (lambda ()
	      (setq lsp-ui-doc-border (face-foreground 'default))
	      (set-face-background 'lsp-ui-doc-background
				   (face-background 'tooltip)))))

(use-package company-lsp
  :init
  (setq company-lsp-cache-candidates 'auto)
  :config
  ;; WORKAROUND:Fix tons of unrelated completion candidates shown
  ;; when a candidate is fulfilled
  ;; @see https://github.com/emacs-lsp/lsp-python-ms/issues/79
  (add-to-list 'company-lsp-filter-candidates '(mspyls))

  (with-no-warnings
    (defun my-company-lsp--on-completion (response prefix)
      "Handle completion RESPONSE.

PREFIX is a string of the prefix when the completion is requested.

Return a list of strings as the completion candidates."
      (let* ((incomplete (and (hash-table-p response) (gethash "isIncomplete" response)))
	         (items (cond ((hash-table-p response) (gethash "items" response))
			              ((sequencep response) response)))
	         (candidates (mapcar (lambda (item)
				                   (company-lsp--make-candidate item prefix))
				                 (lsp--sort-completions items)))
	         (server-id (lsp--client-server-id (lsp--workspace-client lsp--cur-workspace)))
	         (should-filter (or (eq company-lsp-cache-candidates 'auto)
				                (and (null company-lsp-cache-candidates)
				                     (company-lsp--get-config company-lsp-filter-candidates server-id)))))
	    (when (null company-lsp--completion-cache)
	      (add-hook 'company-completion-cancelled-hook #'company-lsp--cleanup-cache nil t)
	      (add-hook 'company-completion-finished-hook #'company-lsp--cleanup-cache nil t))
	    (when (eq company-lsp-cache-candidates 'auto)
	      ;; Only cache candidates on auto mode. If it's t company caches the
	      ;; candidates for us.
	      (company-lsp--cache-put prefix (company-lsp--cache-item-new candidates incomplete)))
	    (if should-filter
	        (company-lsp--filter-candidates candidates prefix)
	      candidates)))
    (advice-add #'company-lsp--on-completion :override #'my-company-lsp--on-completion)))

;; TabNine::Enable in `lsp-mode'
(advice-add #'lsp--auto-configure :after #'my-company-enbale-yas)
;; ;; `:separate`  separately Sort different backgrounds
;; (add-to-list 'company-backends '(company-lsp :with company-tabnine :separate))

;; -------- lsp-python --------

;; -------- eglot with python-language-server[all] --------
(add-hook 'python-mode-hook
	      '(lambda ()
	         (lsp)
	         (push '(company-lsp :with company-tabnine :separate)
	     	       company-backends)
	         (when (string-equal system-type "gnu/linux")
	           (require 'pyvenv)
	           (pyvenv-activate "~/miniconda2/envs/python3")
	           (add-to-list 'eglot-server-programs
			                '((python-mode)
			                  . ("~/miniconda2/envs/python3/bin/pyls" "-v" "--tcp" "--host"
				                 "localhost" "--port" :autoport)))
	           (eglot-ensure))))

;; -------- lsp --------
;; github.com/emacs-lsp/lsp-python-ms
(use-package lsp-python-ms
  ;; Need to demand otherwise this never gets loaded.
  :demand t
  ;; But only after lsp otherwise for some cryptic emacsy reason this doesn't work properly.
  :after lsp python
  ;; :if (or *python3* *python*)
  :init
  (when (string-equal system-type "gnu/linux")
    (require 'pyvenv)
    (pyvenv-activate "~/miniconda2/envs/python3"))
  (when (executable-find "python3")
    (setq lsp-python-ms-python-executable-cmd "python3"))
  (setq-default lsp-pyls-plugins-pylint-enabled nil
		        lsp-pyls-configuration-sources ["flake8"])
  :hook
  (python-mode . (lambda ()
		           (require 'lsp-python-ms)
		           (lsp)
		           (aggressive-indent-mode -1)))
  :config
  (setq lsp-python-ms-dir "~/ms-py-ls/output/bin/Release/linux-x64/publish/"
  	    lsp-python-ms-executable "~/ms-py-ls/output/bin/Release/linux-x64/publish/Microsoft.Python.LanguageServer"))

;; github.com/xhcoding/ms-python
(use-package ms-python
  :load-path "site-lisp"
  :init
  (use-package lsp-mode)
  (use-package lsp-ui
    :config
    (setq lsp-ui-doc-max-height 20
	      lsp-ui-doc-max-width 50
	      lsp-ui-sideline-ignore-duplicate t
	      lsp-ui-peek-always-show t))
  (use-package company-lsp
    :commands
    (company-lsp))
  (use-package company-box
    :hook (company-mode . company-box-mode))
  :hook
  (python-mode . lsp)
  :config
  (setq ms-python-server-install-dir "~/.emacs.d/site-lisp/"))

;; -------- lsp-R --------
(use-package lsp-mode
  :commands lsp
  :config
  (lsp-register-client
   (make-lsp-client :new-connection
			        (lsp-stdio-connection '("R" "--slave" "-e" "languageserver::run()"))
			        :major-modes '(ess-r-mode inferior-ess-r-mode)
			        :server-id 'lsp-R)))

(provide '+lsp)
